/*
* Author: honrun
*/
#include "gd32f3x0.h"
#include "stdint.h"
#include "stdio.h"
#include "string.h"
#include "DevicesUart.h"


uint8_t g_USART0ReadDMABuff[USART0_DMA_READ_LENGTH + 4] = {0};
uint8_t g_USART0SendDMABuff[USART0_DMA_SEND_LENGTH + 4] = {0};
uint8_t g_USART1ReadDMABuff[USART1_DMA_READ_LENGTH + 4] = {0};
uint8_t g_USART1SendDMABuff[USART1_DMA_SEND_LENGTH + 4] = {0};


void vUart0Init(void)
{
    /* enable COM GPIO clock */
    rcu_periph_clock_enable(RCU_GPIOA);
    /* enable USART clock */
    rcu_periph_clock_enable(RCU_USART0);


    /* connect port to USARTx_Tx */
    gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_9);
    /* connect port to USARTx_Rx */
    gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_10);

    /* configure USART Tx as alternate function push-pull */
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_9);

    /* configure USART Rx as alternate function push-pull */
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_10);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_10);


    /* USART configure */
    usart_deinit(USART0);
    usart_word_length_set(USART0, USART_WL_8BIT);
    usart_stop_bit_set(USART0, USART_STB_1BIT);
    usart_parity_config(USART0, USART_PM_NONE);
    usart_baudrate_set(USART0, 115200);

    usart_receive_config(USART0, USART_RECEIVE_ENABLE);
    usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);

    // nvic_irq_enable(USART0_IRQn, 5, 0);
    // usart_flag_clear(USART0, USART_FLAG_IDLE);
    // usart_interrupt_enable(USART0, USART_INT_IDLE);


    vUart0DMAInit();


    usart_enable(USART0);
}

void vUart0DMAInit(void)
{
    dma_parameter_struct dma_init_struct = {0};

    /* enable DMA clock */
    rcu_periph_clock_enable(RCU_DMA);

    /* deinitialize DMA channelx(USARTx tx) */
    dma_deinit(DMA_CH1);
    dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
    dma_init_struct.memory_addr = (uint32_t)g_USART0SendDMABuff;
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.number = USART0_DMA_SEND_LENGTH;
    dma_init_struct.periph_addr = (uint32_t)(&USART_TDATA(USART0));
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
    dma_init(DMA_CH1, &dma_init_struct);

    /* configure DMA mode */
    dma_circulation_disable(DMA_CH1);
    /* enable DMA channel3 */
    dma_channel_disable(DMA_CH1);
    /* USART DMA enable for transmission */
    usart_dma_transmit_config(USART0, USART_DENT_ENABLE);


    /* deinitialize DMA channelx (USARTx rx) */
    dma_deinit(DMA_CH2);
    dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
    dma_init_struct.memory_addr = (uint32_t)g_USART0ReadDMABuff;
    dma_init_struct.number = USART0_DMA_READ_LENGTH;
    dma_init_struct.periph_addr = (uint32_t)(&USART_RDATA(USART0));
    dma_init(DMA_CH2, &dma_init_struct);

    /* configure DMA mode */
    dma_circulation_enable(DMA_CH2);
    /* enable DMA channel4 */
    dma_channel_enable(DMA_CH2);
    /* USART DMA enable for reception */
    usart_dma_receive_config(USART0, USART_DENR_ENABLE);
}

void vUart1Init(void)
{
    /* enable COM GPIO clock */
    rcu_periph_clock_enable(RCU_GPIOA);
    /* enable USART clock */
    rcu_periph_clock_enable(RCU_USART1);


    /* connect port to USARTx_Tx */
    gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_2);
    /* connect port to USARTx_Rx */
    gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_3);

    /* configure USART Tx as alternate function push-pull */
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_2);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_2);

    /* configure USART Rx as alternate function push-pull */
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_3);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_3);


    /* USART configure */
    usart_deinit(USART1);
    usart_word_length_set(USART1, USART_WL_8BIT);
    usart_stop_bit_set(USART1, USART_STB_1BIT);
    usart_parity_config(USART1, USART_PM_NONE);
    usart_baudrate_set(USART1, 115200);

    usart_receive_config(USART1, USART_RECEIVE_ENABLE);
    usart_transmit_config(USART1, USART_TRANSMIT_ENABLE);

    nvic_irq_enable(USART1_IRQn, 5, 0);
    usart_flag_clear(USART1, USART_FLAG_IDLE);
    usart_interrupt_enable(USART1, USART_INT_IDLE);


    vUart1DMAInit();


    usart_enable(USART1);
}

void vUart1DMAInit(void)
{
    dma_parameter_struct dma_init_struct = {0};

    /* enable DMA clock */
    rcu_periph_clock_enable(RCU_DMA);

    /* deinitialize DMA channelx(USARTx tx) */
    dma_deinit(DMA_CH3);
    dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
    dma_init_struct.memory_addr = (uint32_t)g_USART1SendDMABuff;
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.number = USART1_DMA_SEND_LENGTH;
    dma_init_struct.periph_addr = (uint32_t)(&USART_TDATA(USART1));
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
    dma_init(DMA_CH3, &dma_init_struct);

    /* configure DMA mode */
    dma_circulation_disable(DMA_CH3);
    /* enable DMA channel3 */
    dma_channel_disable(DMA_CH3);
    /* USART DMA enable for transmission */
    usart_dma_transmit_config(USART1, USART_DENT_ENABLE);


    /* deinitialize DMA channelx (USARTx rx) */
    dma_deinit(DMA_CH4);
    dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
    dma_init_struct.memory_addr = (uint32_t)g_USART1ReadDMABuff;
    dma_init_struct.number = USART1_DMA_READ_LENGTH;
    dma_init_struct.periph_addr = (uint32_t)(&USART_RDATA(USART1));
    dma_init(DMA_CH4, &dma_init_struct);

    /* configure DMA mode */
    dma_circulation_enable(DMA_CH4);
    /* enable DMA channel4 */
    dma_channel_enable(DMA_CH4);
    /* USART DMA enable for reception */
    usart_dma_receive_config(USART1, USART_DENR_ENABLE);
}

void vUartSendDatas(uint32_t uiUsartPeriph, uint8_t *pucDatas, int32_t iLength)
{
    while((iLength--) > 0)
    {
        /* Transmit Data */
        while((RESET == usart_flag_get(uiUsartPeriph, USART_FLAG_TBE)) && (USART_CTL0(uiUsartPeriph) & USART_CTL0_UEN) && (USART_CTL0(uiUsartPeriph) & USART_TRANSMIT_ENABLE));

        USART_TDATA(uiUsartPeriph) = *pucDatas++;
    }
}

void vUartSendStrings(uint32_t uiUsartPeriph, char *pcStrings)
{
    vUartSendDatas(uiUsartPeriph, (uint8_t *)pcStrings, strlen(pcStrings));
}

/**
 * @brief  设置DMA的数据传输长度，并启动DMA传输
 * @param  DMAy_Channelx：   相应DMA的通道
           Length：          传输长度
 * @retval 无
 */
static void vUartDMASetAddrAndLenthStart(dma_channel_enum channelx, int8_t cDirection, uint32_t uiSourceAddress, uint32_t uiTargetAddress, uint32_t uiLength)
{
    uint32_t uiTime = 0;

    uiTime = 1000000;
    /* Disable the peripheral */
    dma_channel_disable(channelx);
    while(((DMA_CHCTL(channelx) & DMA_CHXCTL_CHEN) != 0) && (--uiTime));

    /* 必须要清除所有中断状态，才允许使能DMA */
    dma_interrupt_flag_clear(channelx, 0xFFFFFFFF);

    /* configure memory base address */
    dma_memory_address_config(channelx, uiSourceAddress);

    /* configure peripheral base address */
    dma_periph_address_config(channelx, uiTargetAddress);

    /* Configure DMA Stream data length */
    dma_transfer_number_config(channelx, uiLength);

    /* configure the direction of data transfer on the channel   */
    dma_transfer_direction_config(channelx, cDirection);

    uiTime = 1000000;
    /* Enable the Peripheral */
    dma_channel_enable(channelx);
    while(((DMA_CHCTL(channelx) & DMA_CHXCTL_CHEN) == 0) && (--uiTime));
}

void vUartDMASendDatas(uint32_t uiUsartPeriph, uint8_t *pucDatas, int32_t iLength)
{
    dma_channel_enum enumChannelx = DMA_CH1;
    uint32_t dma_periph = 0;
    int32_t iDMAMaxLength = 0;
    uint8_t *pucSendDatasHandle = pucDatas;

    if(iLength <= 0)
        return;

    switch(uiUsartPeriph)
    {
        case USART0: enumChannelx = DMA_CH1; pucSendDatasHandle = g_USART0SendDMABuff; iDMAMaxLength = USART0_DMA_SEND_LENGTH; break;
        case USART1: enumChannelx = DMA_CH3; pucSendDatasHandle = g_USART1SendDMABuff; iDMAMaxLength = USART1_DMA_SEND_LENGTH; break;

        default : printf("vUartDMASendDatas channel error.\r\n"); return;
    }

    /* 等待上次DMA传输完成 */
    while((dma_flag_get(enumChannelx, DMA_FLAG_FTF) == RESET) && ((DMA_CHCTL(enumChannelx) & DMA_CHXCTL_CHEN) != 0));

    /* 方式1：充分利用DMA不占用CPU的时间 */
    if(iLength <= iDMAMaxLength)
    {
        memcpy(pucSendDatasHandle, pucDatas, iLength);

        /* 设置传输地址与长度，并启动DMA发送 */
        vUartDMASetAddrAndLenthStart(enumChannelx, DMA_MEMORY_TO_PERIPHERAL, (uint32_t)pucSendDatasHandle, (uint32_t)(&USART_TDATA(uiUsartPeriph)), iLength);
    }
    /* 方式2：发送后再等待，防止发送的数据缓存被之后的代码更改 */
    else
    {
        /* 设置传输地址与长度，并启动DMA发送 */
        vUartDMASetAddrAndLenthStart(enumChannelx, DMA_MEMORY_TO_PERIPHERAL, (uint32_t)pucDatas, (uint32_t)(&USART_TDATA(uiUsartPeriph)), iLength);

        /* 等待本次DMA传输完成 */
        while(dma_flag_get(enumChannelx, DMA_FLAG_FTF) == RESET);
    }
}

void vUartDMASendStrings(uint32_t uiUsartPeriph, char *pcStrings)
{
    vUartDMASendDatas(uiUsartPeriph, (uint8_t *)pcStrings, strlen(pcStrings));
}
